{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Two-Qubit Characterization Sequences\n",
    "Examples of two-qubit sequences, including CR gates"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from QGL import *"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "See Auspex [example notebooks](https://github.com/BBN-Q/Auspex/tree/develop/doc/examples) on how to configure a channel library.\n",
    "\n",
    "For the examples in this notebook, we will use a pre-generated channel library:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cl = ChannelLibrary(db_resource_name=\"./example.sqlite\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1 = cl.new_qubit(\"q1\")  #initialize qubit, e.g. \"q1\", \"q2\", etc.\n",
    "q2 = cl.new_qubit(\"q2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "One can define simultaneous operations on qubits using the `*` operator (indicating a tensor product), see  [ex1_QGL_basics](./ex1_QGL_basics.ipynb). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Below is an example of QGL basic sequence for two qubits. \n",
    "* The first argument (a tuple) selects the driven qubits\n",
    "* The optional argument `measChans` selects which qubits to measure (default = all driven)\n",
    "* The optional argument `docals` adds reference segments corresponding to the computational states at the end of the sequence, used to normalize the readout signals"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "RabiPoints = 101;\n",
    "RabiAmp_NQubits((q1,q2),np.linspace(0,1,RabiPoints), measChans=(q1,q2), docals=True, showPlot=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Two-qubit gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To write a two-qubit gate in QGL, you must add to your channel library a logical channel\n",
    "representing the coupling between qubits. In QGL terminology, this is known as\n",
    "an `Edge`, and is a *directed* edge in the connectivity graph of your device.\n",
    "QGL uses directed edges because certain two-qubit interactions have a preferred\n",
    "ordering of the interaction. For instance, a cross resonance gate has a\n",
    "preferred sign of the qubit-qubit detuning. By storing directed edges, we can\n",
    "write two-qubit primitives that emit different pulses depending on whether the\n",
    "(control, target) pair is aligned or anti-aligned with the underlying\n",
    "interaction Hamiltonian.\n",
    "\n",
    "The following examples declares that q1 and q2 are connected, and defines an edge \n",
    "connecting from q1 to q2:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cl.new_edge(q1, q2) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now include `CNOT` gates in our sequences. In this example you can see the use of the two-qubit primitive `CNOT`.\n",
    "The exact sequence will depend on the (source,\n",
    "target) order you selected in creating the q1-q2 `Edge` and on the \n",
    "chosen `cnot_implementation`. \n",
    "\n",
    "You can select a\n",
    "different default `CNOT` implementation by modifying the [cnot_implementation](https://github.com/BBN-Q/QGL/blob/3dce4ec0996ee010f8a80f3a64e585b1f7b9f7d3/QGL/config.py#L34) key\n",
    "in your local QGL's `config.py` file. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seqs = [[Id(q1), CNOT(q1, q2)]] # use the default CNOT_simple implementation, where the CNOT is represented as an X pulse\n",
    "mf = compile_to_hardware(seqs,'CNOT_simple')  \n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also explicitly call `CNOT_CR` to \n",
    "use the CR decomposition, independently of the global configuration. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seqs = [[Id(q1), CNOT_CR(q1, q2)]] # use the CNOT_CR implementation, where the CNOT is decomposed \n",
    "# into a sequence of single-qubit gates and a ZX90, as is appropriate for a cross-resonance interaction.\n",
    "mf = compile_to_hardware(seqs,'CNOT_CR')  \n",
    "plot_pulse_files(mf)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Inverting the order of the `CNOT_CR` input will also produce a CNOT using the same directed edge (q1->q2), but with added single-qubit gates to invert the CNOT control and target. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "seqs = [[Id(q1), CNOT_CR(q2, q1)]] # use the CNOT_CR implementation, where the CNOT is decomposed \n",
    "# into a sequence of single-qubit gates and a ZX90, as is appropriate for a cross-resonance interaction.\n",
    "mf = compile_to_hardware(seqs,'CNOT_CR_inv')  \n",
    "plot_pulse_files(mf)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
